# -*- coding:utf-8 -*-
# @Time     :2024/3/14 22:36
# @Author   :CHNJX
# @File     :log_analyzer.py
# @Desc     :

import datetime
import re
import pathlib
from collections import defaultdict


class LogAnalyzer:
    IGNORE_LOG_CONTENT = [
        'Invalid Input:iEventMask:ffffffff',
        'Skip time change',
        'No data break continue',
        'cannot read,0,errno 0 Success',
        'ioctl SIOCSIFADDR wlan0 failed by No such device',
        'vprc_storage_next_dblock|1209|TSDK-Lv3',
        'vpmid_sys_switch_runmdoe|435|TSDK-Lv3',  # 切换页面调试打印
        'vpmid_sys_switch_resmode|481|TSDK-Lv3',
        'vpmid_sys_switch_runmdoe|437|TSDK-Lv3',
        'vpmid_sys_switch_resmode|483|TSDK-Lv3',
        'vpmid_sys_switch_runmdoe|443|TSDK-Lv3',
        'vpmid_sys_switch_resmode|484|TSDK-Lv3',
        'vpmid_sys_switch_runmdoe|444|TSDK-Lv3',
        'vpmid_sys_switch_runmdoe|475|TSDK-Lv3',
        'vpmid_sys_switch_resmode|515|TSDK-Lv3',
        'vpmid_sys_switch_runmdoe|510|TSDK-Lv3',
        'vpmid_sys_switch_resmode|550|TSDK-Lv3',
        'vpmid_sys_switch_runmdoe|511|TSDK-Lv3',
        'vpmid_sys_switch_resmode|551|TSDK-Lv3',
        'vpmid_prw_update_ch_streamtype|274|TSDK-Lv3',
        'vpmid_pbstream_cb|298|TSDK-Lv3',  # 回放点击暂停
        'lv_linux_fbdev_set_file|410|TSDK-Lv3',  # 切换分辨率
        'gui_scr_style_set_theme|1152|TSDK-Lv3',  # 切换分辨率
        'lv_evdev_event_get|97|TSDK-Lv3',  # 鼠标操作打印
        'vprc_storage_next_dblock|1210|TSDK-Lv3'  # 硬盘换块写入
    ]
    
    # 错误日志分类定义
    ERROR_CATEGORIES = {
        "硬盘错误": [
            "storage", "disk", "HDD", "SSD", "write error", "read error", 
            "I/O error", "cannot open", "disk full", "no space"
        ],
        "系统异常": [
            "crash", "exception", "SIGSEGV", "SIGABRT", "core dump", 
            "stack overflow", "memory leak", "segmentation fault"
        ],
        "网络问题": [
            "network", "connection refused", "timeout", "unreachable", 
            "DNS", "socket", "bind failed", "connect failed"
        ],
        "设备错误": [
            "device", "hardware", "camera", "sensor", "disconnect", 
            "not responding", "device error"
        ],
        "权限问题": [
            "permission denied", "access denied", "unauthorized", 
            "forbidden", "no permission"
        ]
    }
    
    # 严重程度评分（1-5，5最严重）
    ERROR_SEVERITY = {
        "硬盘错误": 5,
        "系统异常": 4,
        "网络问题": 3,
        "设备错误": 4,
        "权限问题": 2,
        "其他错误": 1
    }
    
    DATE_FORMAT = "%Y-%m-%d"
    LOG_PATTERN = re.compile(r'.*?\[(\d{4}/\d{2}/\d{2}-\d{2}:\d{2}:\d{2})\|(.*?)\|(\d+)\|(TSDK-Lv[123])\](.*)')

    def __init__(self, log_directory_path):
        self.log_directory_path = pathlib.Path(log_directory_path)
        # 错误模式识别：记录常见错误模式的出现频率
        self.error_patterns = defaultdict(int)
        # 设备错误历史：记录每个设备的错误历史
        self.device_error_history = defaultdict(list)

    def read_logs(self, log_file_path):
        """读取日志文件的内容"""
        try:
            with open(log_file_path, 'r', encoding='utf-8') as file:
                return file.readlines()
        except (IOError, UnicodeDecodeError) as e:
            print(f"Error opening or decoding log file {log_file_path}: {e}")
            return []

    def categorize_error(self, log_message):
        """根据关键词将错误分类"""
        for category, keywords in self.ERROR_CATEGORIES.items():
            if any(keyword.lower() in log_message.lower() for keyword in keywords):
                return category
        return "其他错误"
    
    def calculate_error_score(self, error_type, frequency, level):
        """计算错误的严重性评分"""
        # 基础分数为错误类型的严重度
        base_score = self.ERROR_SEVERITY.get(error_type, 1)
        
        # 根据日志级别调整分数
        level_multiplier = 1.0
        if "Lv1" in level:  # 最严重的日志级别
            level_multiplier = 1.5
        elif "Lv2" in level:
            level_multiplier = 1.2
            
        # 根据频率调整分数：频率越高，分数越高
        frequency_factor = min(frequency / 5, 2.0)  # 最多翻倍
        
        # 计算最终分数
        final_score = base_score * level_multiplier * frequency_factor
        
        return round(final_score, 1)  # 四舍五入到一位小数

    def process_logs(self, file_path, ignore_log_content):
        seen_logs = set()
        log_errors = defaultdict(list)
        log_counts = defaultdict(lambda: defaultdict(int))  # 新增加一个字典用于统计错误日志的次数
        error_details = defaultdict(list)  # 存储详细的错误信息
        max_input_time = 0
        device_name = pathlib.Path(file_path).stem.split('_')[0]  # 从文件名提取设备名称

        with open(file_path, 'r', encoding='utf-8') as file:
            for line in file:
                if any(ign_content in line for ign_content in ignore_log_content):
                    continue  # 忽略当前行
                # clear line like [08:48:37.689]
                # 日志格式：[2024/05/30-06:01:00|vpml_time_check_date|70|TSDK-Lv2]Invalid date:3924-02-01 00:00:00
                line = re.sub(r'\[(\d{2}:\d{2}:\d{2}\.\d{3})\]', '', line)
                line = re.sub(r'\[(\d{4}/\d{2}/\d{2}-\d{2}:\d{2}:\d{2})\]', '', line)
                line = re.sub(r'\[(\d{2}:\d{2}:\d{2})\]', '', line)

                match = self.LOG_PATTERN.match(line)
                if match:
                    timestamp, log_type, num, level, message = match.groups()

                    # 更新最大的input time值
                    input_time_match = re.search(r'input time (\d+)ms', message)
                    if input_time_match:
                        input_time = int(input_time_match.group(1))
                        max_input_time = max(max_input_time, input_time)

                    # 构建唯一标识符用于去重
                    log_key = f"{log_type}|{num}|{level}"
                    message_clean = message.strip()

                    # 如果已处理过相同类型的日志，则只增加计数
                    if log_key not in seen_logs:
                        seen_logs.add(log_key)
                        
                        # 确定错误类型和分类存储日志
                        error_category = self.categorize_error(message_clean)
                        is_storage_related = 'storage' in log_type.lower() or error_category == "硬盘错误"
                        error_type = '硬盘信息' if is_storage_related else '昨日日志'
                        
                        # 记录错误模式
                        self.error_patterns[log_key] += 1
                        
                        # 记录设备错误历史
                        self.device_error_history[device_name].append((timestamp, error_category, message_clean))
                        
                        # 添加错误信息
                        log_errors[error_type].append(log_key)
                        
                        # 保存详细错误信息
                        error_details[error_type].append({
                            'key': log_key,
                            'message': message_clean,
                            'timestamp': timestamp,
                            'category': error_category,
                            'level': level
                        })

                    # 无论是否已处理过，都增加该类型日志的计数
                    if log_key:
                        is_storage_related = 'storage' in log_type.lower()
                        error_type = '硬盘信息' if is_storage_related else '昨日日志'
                        log_counts[error_type][log_key] += 1

        # 根据是否有错误信息设置结果状态
        log_result = "pass" if not log_errors['昨日日志'] else "fail"
        storage_result = "pass" if not log_errors['硬盘信息'] else "fail"

        # 处理错误信息，添加分类和严重性评分
        enhanced_errors = {
            '昨日日志': [],
            '硬盘信息': []
        }
        
        for error_type in ['昨日日志', '硬盘信息']:
            for error_info in error_details.get(error_type, []):
                error_key = error_info['key']
                frequency = log_counts[error_type][error_key]
                category = error_info['category']
                score = self.calculate_error_score(category, frequency, error_info['level'])
                
                # 格式化错误信息
                formatted_error = (
                    f"{error_key}; "
                    f"出现次数: {frequency}; "
                    f"详情: {error_info['message'][:100]}..."  # 截取消息的前100个字符
                )
                
                enhanced_errors[error_type].append((score, formatted_error))
        
        # 按严重度排序错误信息（从高到低）
        for error_type in enhanced_errors:
            enhanced_errors[error_type].sort(reverse=True)  # 按评分降序排序
            enhanced_errors[error_type] = [err[1] for err in enhanced_errors[error_type]]  # 只保留格式化的错误信息
            # 添加序号到错误信息前
            enhanced_errors[error_type] = [f"{i+1}. {err}" for i, err in enumerate(enhanced_errors[error_type])]
        
        # 添加最大输入时间
        if max_input_time > 0:
            enhanced_errors['硬盘信息'].append(f"Max input time: {max_input_time} ms")

        # 分析设备历史错误模式
        if device_name in self.device_error_history and len(self.device_error_history[device_name]) > 1:
            error_pattern_summary = self.analyze_error_patterns(device_name)
            if error_pattern_summary:
                enhanced_errors['昨日日志'].append(f"错误模式分析: {error_pattern_summary}")

        return log_result, storage_result, enhanced_errors['昨日日志'], enhanced_errors['硬盘信息']
    
    def analyze_error_patterns(self, device_name):
        """分析设备的错误模式"""
        errors = self.device_error_history.get(device_name, [])
        if not errors:
            return None
            
        # 统计错误类别
        category_counts = defaultdict(int)
        for _, category, _ in errors:
            category_counts[category] += 1
            
        # 找出最常见的错误类别
        most_common = max(category_counts.items(), key=lambda x: x[1]) if category_counts else None
        
        if most_common and most_common[1] > 1:
            return f"此设备频繁出现{most_common[0]}类错误 (共{most_common[1]}次)"
        return None

    def analyze_all_logs(self, before_date: int = 1):
        """分析所有日志文件"""
        print(f"\n分析日志目录: {self.log_directory_path}")
        
        # 获取目标日期
        target_date = datetime.datetime.now() - datetime.timedelta(days=before_date)
        target_date_str = target_date.strftime("%Y-%m-%d")
        print(f"目标日期: {target_date_str}")
        
        # 构建日期目录路径
        date_dir = self.log_directory_path / target_date_str
        print(f"日期目录: {date_dir}")
        
        if not date_dir.exists():
            print(f"警告: 日期目录不存在: {date_dir}")
            return {}
            
        # 获取所有日志文件
        # target_date 只保留月-日，并将连字符替换为下划线
        target_date_str = target_date.strftime("%m-%d")
        log_files = list(date_dir.glob(f"*_{target_date_str}.log"))  # 使用 f-string 而不是 ${} 语法
        print(f"找到 {len(log_files)} 个日志文件:")
        for log_file in log_files:
            print(f"  - {log_file.name}")
        
        if not log_files:
            print("警告: 未找到日志文件")
            return {}
            
        results = {}
        for log_file in log_files:
            print(f"\n处理日志文件: {log_file.name}")
            try:
                # 处理日志文件
                log_result = self.process_logs(log_file, self.IGNORE_LOG_CONTENT)
                if log_result:
                    device_name = log_file.stem.split('_')[0]  # 从文件名提取设备名称
                    results[device_name] = log_result
                    print(f"成功处理设备 {device_name} 的日志")
            except Exception as e:
                print(f"处理日志文件 {log_file} 时出错: {e}")
                continue
        
        # 添加跨设备分析
        if results:
            self.add_cross_device_analysis(results)
            # print(f"\n分析结果: {results}")
        else:
            print("警告: 没有找到任何有效的日志数据")
            
        return results
        
    def add_cross_device_analysis(self, data):
        """添加跨设备的错误模式分析"""
        # 收集所有设备的错误
        all_errors = defaultdict(list)
        for device, device_data in data.items():
            for error_type in ['昨日日志', '硬盘信息']:
                if error_type in device_data and device_data[error_type][1]:
                    error_lines = device_data[error_type][1].split('\n')
                    for line in error_lines:
                        if line.strip():
                            all_errors[error_type].append((device, line))
        
        # 查找多个设备共有的错误模式
        if len(data) > 1:  # 只在有多个设备时分析
            for error_type, errors in all_errors.items():
                if len(errors) > 1:
                    # 分析错误信息中的相似性
                    error_similarities = self.find_common_errors(errors)
                    
                    if error_similarities:
                        # 将分析结果添加到每个设备的数据中
                        for device in data:
                            if error_type in data[device] and data[device][error_type][1]:
                                summary = f"多设备错误模式:\n{error_similarities}"
                                if data[device][error_type][1]:
                                    data[device][error_type] = (
                                        data[device][error_type][0],
                                        data[device][error_type][1] + f"\n\n{summary}"
                                    )
                                else:
                                    data[device][error_type] = (
                                        data[device][error_type][0],
                                        summary
                                    )
    
    def find_common_errors(self, device_errors):
        """查找设备间共有的错误模式"""
        # 提取错误消息的关键部分进行比较
        error_keys = defaultdict(list)
        for device, error_line in device_errors:
            # 尝试提取关键错误信息
            parts = error_line.split(';')
            if len(parts) > 0:
                key_part = parts[0].strip()
                if '|' in key_part:  # 提取错误类型
                    error_keys[key_part].append(device)
        
        # 查找多个设备共有的错误
        common_errors = []
        for error_key, devices in error_keys.items():
            if len(set(devices)) > 1:  # 如果错误出现在多个设备上
                common_errors.append(f"{error_key} (出现在设备: {', '.join(set(devices))})")
        
        if common_errors:
            # 添加序号到每个错误前面
            numbered_errors = [f"{i+1}. {error}" for i, error in enumerate(common_errors[:3])]
            return "; ".join(numbered_errors)  # 最多返回前3个共有错误
        return None
